package main

import (
	"fmt"
	"sync"
	"testing"
)

//Singleton 是单例模式类
type Singleton struct{}

var singleton *Singleton
var once sync.Once

//GetInstance 用于获取单例模式对象
func GetInstance() *Singleton {
	once.Do(func() {
		singleton = &Singleton{}
	})

	return singleton
}

const parCount = 100

func TestSingleton() {
	t := &testing.T{}
	ins1 := GetInstance()
	ins2 := GetInstance()
	if ins1 != ins2 {
		t.Fatal("instance is not equal")
	} else {
		fmt.Println(ins2)
	}
}

func TestParallelSingleton() {
	t := &testing.T{}
	wg := sync.WaitGroup{}
	wg.Add(parCount)
	instances := [parCount]*Singleton{}
	for i := 0; i < parCount; i++ {
		go func(index int) {
			instances[index] = GetInstance()
			wg.Done()
		}(i)
	}
	wg.Wait()
	for i := 1; i < parCount; i++ {
		if instances[i] != instances[i-1] {
			t.Fatal("instance is not equal")
		} else {
			fmt.Println("是同一个对象")
		}
	}
}

func main() {
	TestSingleton()
	TestParallelSingleton()
}

/*
单例模式
单例（Singleton）模式的定义：指一个类只有一个实例，且该类能自行创建这个实例的一种模式。
单例模式有 3 个特点：
单例类只有一个实例对象；
该单例对象必须由单例类自行创建；
单例类对外提供一个访问该单例的全局访问点。

单例模式的优点：
单例模式可以保证内存里只有一个实例，减少了内存的开销。
可以避免对资源的多重占用。
单例模式设置全局访问点，可以优化和共享资源的访问。

单例模式的缺点：
单例模式一般没有接口，扩展困难。如果要扩展，则除了修改原来的代码，没有第二种途径，违背开闭原则。
在并发测试中，单例模式不利于代码调试。在调试过程中，如果单例中的代码没有执行完，也不能模拟生成一个新的对象。
单例模式的功能代码通常写在一个类中，如果功能设计不合理，则很容易违背单一职责原则。

# 单例模式
使用懒惰模式的单例模式，使用双重检查加锁保证线程安全

*/
